const chalk = require('chalk');
// const packagejs = require('../../package.json');
const BaseGenerator = require('generator-jhipster/generators/generator-base');
// const jhipsterConstants = require('generator-jhipster/generators/generator-constants');
const _ = require('lodash');
const pluralize = require('pluralize');
const jhiCore = require('jhipster-core');
const statistics = require('generator-jhipster/generators/statistics');
const constants = require('generator-jhipster/generators/generator-constants');
// eslint-disable-next-line no-unused-vars
const SUPPORTED_VALIDATION_RULES = constants.SUPPORTED_VALIDATION_RULES;
let useBlueprint;
module.exports = class extends BaseGenerator {
    constructor(args, opts) {
        super(args, opts);

        // This makes `name` a required argument.
        this.argument('name', {
            type: String,
            required: true,
            description: 'Entity name'
        });

        this.context = {};

        this.setupEntityOptions(this, this, this.context);
        this.registerClientTransforms();
        const blueprint = this.config.get('blueprint');
        if (!opts.fromBlueprint) {
            // use global variable since getters dont have access to instance property
            useBlueprint = this.composeBlueprint(blueprint, 'entity', {
                'skip-install': this.options['skip-install'],
                'from-cli': this.options['from-cli'],
                force: this.options.force,
                entity: this.options.entity,
                arguments: [this.context.name]
            });
        } else {
            useBlueprint = false;
        }
    }
    get initializing() {
        return {
            readConfig() {
                // this.entityConfig = this.options.entityConfig;
                this.jhipsterAppConfig = this.getJhipsterAppConfig();
                if (!this.jhipsterAppConfig) {
                    this.error('Can\'t read .yo-rc.json');
                }
            },
            displayLogo() {
                // this.log(chalk.white(`Running ${chalk.bold('JHipster rain')} Generator! ${chalk.yellow(`v${packagejs.version}\n`)}`));
            },
            validate() {
                // this shouldn't be run directly
                // if (!this.entityConfig) {
                //     this.env.error(`${chalk.red.bold('ERROR!')} This sub generator should be used only from JHipster and cannot be run directly...\n`);
                // }
            }
        };
    }

    prompting() {
        // don't prompt if data are imported from a file
        // if (this.entityConfig.useConfigurationFile === true && this.entityConfig.data && typeof this.entityConfig.data.yourOptionKey !== 'undefined') {
        //     this.yourOptionKey = this.entityConfig.data.yourOptionKey;
        //     return;
        // }
        const done = this.async();
        const prompts = [
            // {
            //     type: 'confirm',
            //     name: 'enableOption',
            //     message: 'Some option here?',
            //     default: false
            // }
        ];

        this.prompt(prompts).then((props) => {
            this.props = props;
            // To access props later use this.props.someOption;

            done();
        });
    }


    get configuring() {
        if (useBlueprint) return null;
        return {
            validateFile() {
                const context = this.context;
                if (!context.useConfigurationFile) {
                    return;
                }
                const entityName = context.name;
                // Validate entity json field content
                context.fields.forEach((field) => {
                    if (_.isUndefined(field.fieldName)) {
                        this.error(chalk.red(`fieldName is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                    }

                    if (_.isUndefined(field.fieldType)) {
                        this.error(chalk.red(`fieldType is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                    }

                    if (!_.isUndefined(field.fieldValidateRules)) {
                        if (!_.isArray(field.fieldValidateRules)) {
                            this.error(chalk.red(`fieldValidateRules is not an array in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        field.fieldValidateRules.forEach((fieldValidateRule) => {
                            if (!_.includes(SUPPORTED_VALIDATION_RULES, fieldValidateRule)) {
                                this.error(chalk.red(`fieldValidateRules contains unknown validation rule ${fieldValidateRule} in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)} [supported validation rules ${SUPPORTED_VALIDATION_RULES}]`));
                            }
                        });
                        if (_.includes(field.fieldValidateRules, 'max') && _.isUndefined(field.fieldValidateRulesMax)) {
                            this.error(chalk.red(`fieldValidateRulesMax is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        if (_.includes(field.fieldValidateRules, 'min') && _.isUndefined(field.fieldValidateRulesMin)) {
                            this.error(chalk.red(`fieldValidateRulesMin is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        if (_.includes(field.fieldValidateRules, 'maxlength') && _.isUndefined(field.fieldValidateRulesMaxlength)) {
                            this.error(chalk.red(`fieldValidateRulesMaxlength is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        if (_.includes(field.fieldValidateRules, 'minlength') && _.isUndefined(field.fieldValidateRulesMinlength)) {
                            this.error(chalk.red(`fieldValidateRulesMinlength is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        if (_.includes(field.fieldValidateRules, 'maxbytes') && _.isUndefined(field.fieldValidateRulesMaxbytes)) {
                            this.error(chalk.red(`fieldValidateRulesMaxbytes is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        if (_.includes(field.fieldValidateRules, 'minbytes') && _.isUndefined(field.fieldValidateRulesMinbytes)) {
                            this.error(chalk.red(`fieldValidateRulesMinbytes is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        if (_.includes(field.fieldValidateRules, 'pattern') && _.isUndefined(field.fieldValidateRulesPattern)) {
                            this.error(chalk.red(`fieldValidateRulesPattern is missing in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)}`));
                        }
                        if (field.fieldType === 'ByteBuffer') {
                            this.warning(chalk.red(`Cannot use validation in .jhipster/${entityName}.json for field ${JSON.stringify(field, null, 4)} \nHibernate JPA 2 Metamodel does not work with Bean Validation 2 for LOB fields, so LOB validation is disabled`));
                            field.validation = false;
                            field.fieldValidateRules = [];
                        }
                    }
                });

                // Validate entity json relationship content
                context.relationships.forEach((relationship) => {
                    if (_.isUndefined(relationship.relationshipName)) {
                        relationship.relationshipName = relationship.otherEntityName;
                        this.warning(`relationshipName is missing in .jhipster/${entityName}.json for relationship ${JSON.stringify(relationship, null, 4)}, using ${relationship.otherEntityName} as fallback`);
                    }

                    if (_.isUndefined(relationship.otherEntityName)) {
                        this.error(chalk.red(`otherEntityName is missing in .jhipster/${entityName}.json for relationship ${JSON.stringify(relationship, null, 4)}`));
                    }

                    if (_.isUndefined(relationship.otherEntityRelationshipName)) {
                        if (
                            relationship.relationshipType === 'one-to-many' ||
                            (relationship.relationshipType === 'many-to-many' && relationship.ownerSide === false) ||
                            relationship.relationshipType === 'one-to-one'
                        ) {
                            relationship.otherEntityRelationshipName = _.lowerFirst(entityName);
                            this.warning(`otherEntityRelationshipName is missing in .jhipster/${entityName}.json for relationship ${JSON.stringify(relationship, null, 4)}, using ${_.lowerFirst(entityName)} as fallback`);
                        }
                    }

                    if (
                        _.isUndefined(relationship.otherEntityField) &&
                        (relationship.relationshipType === 'many-to-one' ||
                            (relationship.relationshipType === 'many-to-many' && relationship.ownerSide === true) ||
                            (relationship.relationshipType === 'one-to-one' && relationship.ownerSide === true))
                    ) {
                        this.warning(`otherEntityField is missing in .jhipster/${entityName}.json for relationship ${JSON.stringify(relationship, null, 4)}, using id as fallback`);
                        relationship.otherEntityField = 'id';
                    }

                    if (_.isUndefined(relationship.relationshipType)) {
                        this.error(chalk.red(`relationshipType is missing in .jhipster/${entityName}.json for relationship ${JSON.stringify(relationship, null, 4)}`));
                    }

                    if (
                        _.isUndefined(relationship.ownerSide) &&
                        (relationship.relationshipType === 'one-to-one' || relationship.relationshipType === 'many-to-many')
                    ) {
                        this.error(chalk.red(`ownerSide is missing in .jhipster/${entityName}.json for relationship ${JSON.stringify(relationship, null, 4)}`));
                    }
                });

                // Validate root entity json content
                if (_.isUndefined(context.changelogDate) && (context.databaseType === 'sql' || context.databaseType === 'cassandra')) {
                    const currentDate = this.dateFormatForLiquibase();
                    this.warning(`changelogDate is missing in .jhipster/${entityName}.json, using ${currentDate} as fallback`);
                    context.changelogDate = currentDate;
                }
                if (_.isUndefined(context.dto)) {
                    this.warning(`dto is missing in .jhipster/${entityName}.json, using no as fallback`);
                    context.dto = 'no';
                }
                if (_.isUndefined(context.service)) {
                    this.warning(`service is missing in .jhipster/${entityName}.json, using no as fallback`);
                    context.service = 'no';
                }
                if (_.isUndefined(context.jpaMetamodelFiltering)) {
                    this.warning(`jpaMetamodelFiltering is missing in .jhipster/${entityName}.json, using 'no' as fallback`);
                    context.jpaMetamodelFiltering = false;
                }
                if (_.isUndefined(context.pagination)) {
                    this.warning(`pagination is missing in .jhipster/${entityName}.json, using no as fallback`);
                    context.pagination = 'no';
                }
                if (!context.clientRootFolder && !context.skipUiGrouping) {
                    // if it is a gateway generating from a microservice, or a microservice
                    if (context.useMicroserviceJson || context.applicationType === 'microservice') {
                        context.clientRootFolder = context.microserviceName;
                    }
                }
            },

            writeEntityJson() {
                const context = this.context;
                if (context.useConfigurationFile && context.updateEntity === 'regenerate') {
                    return; // do not update if regenerating entity
                }
                // store information in a file for further use.
                if (!context.useConfigurationFile && ['sql', 'cassandra'].includes(context.databaseType)) {
                    context.changelogDate = this.dateFormatForLiquibase();
                }
                this.data = {};
                this.data.fluentMethods = context.fluentMethods;
                this.data.clientRootFolder = context.clientRootFolder;
                this.data.relationships = context.relationships;
                this.data.fields = context.fields;
                this.data.changelogDate = context.changelogDate;
                this.data.dto = context.dto;
                this.data.searchEngine = context.searchEngine;
                this.data.service = context.service;
                this.data.entityTableName = context.entityTableName;
                this.copyFilteringFlag(context, this.data, context);
                if (['sql', 'mongodb', 'couchbase'].includes(context.databaseType)) {
                    this.data.pagination = context.pagination;
                } else {
                    this.data.pagination = 'no';
                }
                this.data.javadoc = context.javadoc;
                if (context.entityAngularJSSuffix) {
                    this.data.angularJSSuffix = context.entityAngularJSSuffix;
                }
                if (context.applicationType === 'microservice') {
                    this.data.microserviceName = context.baseName;
                }
                if (context.applicationType === 'gateway' && context.useMicroserviceJson) {
                    this.data.microserviceName = context.microserviceName;
                }
                // this.fs.writeJSON(context.filename, this.data, null, 4);
            },

            loadInMemoryData() {
                const context = this.context;
                const entityName = context.name;
                const entityNamePluralizedAndSpinalCased = _.kebabCase(pluralize(entityName));

                context.entityClass = context.entityNameCapitalized;
                context.entityClassHumanized = _.startCase(context.entityNameCapitalized);
                context.entityClassPlural = pluralize(context.entityClass);
                context.entityClassPluralHumanized = _.startCase(context.entityClassPlural);
                context.entityInstance = _.lowerFirst(entityName);
                context.entityInstancePlural = pluralize(context.entityInstance);
                context.entityApiUrl = entityNamePluralizedAndSpinalCased;
                context.entityFileName = _.kebabCase(context.entityNameCapitalized + _.upperFirst(context.entityAngularJSSuffix));
                context.entityFolderName = this.getEntityFolderName(context.clientRootFolder, context.entityFileName);
                context.entityModelFileName = context.entityFolderName;
                context.entityParentPathAddition = this.getEntityParentPathAddition(context.clientRootFolder);
                context.entityPluralFileName = entityNamePluralizedAndSpinalCased + context.entityAngularJSSuffix;
                context.entityServiceFileName = context.entityFileName;
                context.entityAngularName = context.entityClass + this.upperFirstCamelCase(context.entityAngularJSSuffix);
                context.entityReactName = context.entityClass + this.upperFirstCamelCase(context.entityAngularJSSuffix);
                context.entityStateName = _.kebabCase(context.entityAngularName);
                context.entityUrl = context.entityStateName;
                context.entityTranslationKey = context.clientRootFolder
                    ? _.camelCase(`${context.clientRootFolder}-${context.entityInstance}`)
                    : context.entityInstance;
                context.entityTranslationKeyMenu = _.camelCase(context.clientRootFolder ? `${context.clientRootFolder}-${context.entityStateName}` : context.entityStateName);
                context.jhiTablePrefix = this.getTableName(context.jhiPrefix);
                context.reactiveRepositories = context.reactive && ['mongodb', 'cassandra', 'couchbase'].includes(context.databaseType);

                context.fieldsContainDate = false;
                context.fieldsContainInstant = false;
                context.fieldsContainZonedDateTime = false;
                context.fieldsContainLocalDate = false;
                context.fieldsContainBigDecimal = false;
                context.fieldsContainBlob = false;
                context.fieldsContainImageBlob = false;
                context.fieldsContainBlobOrImage = false;
                context.validation = false;
                context.fieldsContainOwnerManyToMany = false;
                context.fieldsContainNoOwnerOneToOne = false;
                context.fieldsContainOwnerOneToOne = false;
                context.fieldsContainOneToMany = false;
                context.fieldsContainManyToOne = false;
                context.fieldsIsReactAvField = false;
                context.blobFields = [];
                context.differentTypes = [context.entityClass];
                // Config extra name
                context.extraName = 'Extra';
                context.extraInstance = 'extra';
                if (!context.relationships) {
                    context.relationships = [];
                }
                context.differentRelationships = {};
                context.i18nToLoad = [context.entityInstance];
                context.i18nKeyPrefix = `${context.angularAppName}.${context.entityTranslationKey}`;

                // Load in-memory data for fields
                // context.fields.forEach((field) => {
                //     // Migration from JodaTime to Java Time
                //     if (field.fieldType === 'DateTime' || field.fieldType === 'Date') {
                //         field.fieldType = 'Instant';
                //     }
                //     const fieldType = field.fieldType;
                //
                //     if (!['Instant', 'ZonedDateTime', 'Boolean'].includes(fieldType)) {
                //         context.fieldsIsReactAvField = true;
                //     }
                //
                //     const nonEnumType = [
                //         'String',
                //         'Integer',
                //         'Long',
                //         'Float',
                //         'Double',
                //         'BigDecimal',
                //         'LocalDate',
                //         'Instant',
                //         'ZonedDateTime',
                //         'Boolean',
                //         'byte[]',
                //         'ByteBuffer'
                //     ].includes(fieldType);
                //     if (['sql', 'mongodb', 'couchbase', 'no'].includes(context.databaseType) && !nonEnumType) {
                //         field.fieldIsEnum = true;
                //     } else {
                //         field.fieldIsEnum = false;
                //     }
                //
                //     if (field.fieldIsEnum === true) {
                //         context.i18nToLoad.push(field.enumInstance);
                //     }
                //
                //     if (_.isUndefined(field.fieldNameCapitalized)) {
                //         field.fieldNameCapitalized = _.upperFirst(field.fieldName);
                //     }
                //
                //     if (_.isUndefined(field.fieldNameUnderscored)) {
                //         field.fieldNameUnderscored = _.snakeCase(field.fieldName);
                //     }
                //
                //     if (_.isUndefined(field.fieldNameAsDatabaseColumn)) {
                //         const fieldNameUnderscored = _.snakeCase(field.fieldName);
                //         const jhiFieldNamePrefix = this.getColumnName(context.jhiPrefix);
                //         if (jhiCore.isReservedTableName(fieldNameUnderscored, context.databaseType)) {
                //             field.fieldNameAsDatabaseColumn = `${jhiFieldNamePrefix}_${fieldNameUnderscored}`;
                //         } else {
                //             field.fieldNameAsDatabaseColumn = fieldNameUnderscored;
                //         }
                //     }
                //
                //     if (_.isUndefined(field.fieldNameHumanized)) {
                //         field.fieldNameHumanized = _.startCase(field.fieldName);
                //     }
                //
                //     if (_.isUndefined(field.fieldInJavaBeanMethod)) {
                //         // Handle the specific case when the second letter is capitalized
                //         // See http://stackoverflow.com/questions/2948083/naming-convention-for-getters-setters-in-java
                //         if (field.fieldName.length > 1) {
                //             const firstLetter = field.fieldName.charAt(0);
                //             const secondLetter = field.fieldName.charAt(1);
                //             if (firstLetter === firstLetter.toLowerCase() && secondLetter === secondLetter.toUpperCase()) {
                //                 field.fieldInJavaBeanMethod = firstLetter.toLowerCase() + field.fieldName.slice(1);
                //             } else {
                //                 field.fieldInJavaBeanMethod = _.upperFirst(field.fieldName);
                //             }
                //         } else {
                //             field.fieldInJavaBeanMethod = _.upperFirst(field.fieldName);
                //         }
                //     }
                //
                //     if (_.isUndefined(field.fieldValidateRulesPatternJava)) {
                //         field.fieldValidateRulesPatternJava = field.fieldValidateRulesPattern
                //             ? field.fieldValidateRulesPattern.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
                //             : field.fieldValidateRulesPattern;
                //     }
                //
                //     field.fieldValidate = _.isArray(field.fieldValidateRules) && field.fieldValidateRules.length >= 1;
                //
                //     if (fieldType === 'ZonedDateTime') {
                //         context.fieldsContainZonedDateTime = true;
                //         context.fieldsContainDate = true;
                //     } else if (fieldType === 'Instant') {
                //         context.fieldsContainInstant = true;
                //         context.fieldsContainDate = true;
                //     } else if (fieldType === 'LocalDate') {
                //         context.fieldsContainLocalDate = true;
                //         context.fieldsContainDate = true;
                //     } else if (fieldType === 'BigDecimal') {
                //         context.fieldsContainBigDecimal = true;
                //     } else if (fieldType === 'byte[]' || fieldType === 'ByteBuffer') {
                //         context.blobFields.push(field);
                //         context.fieldsContainBlob = true;
                //         if (field.fieldTypeBlobContent === 'image') {
                //             context.fieldsContainImageBlob = true;
                //         }
                //         if (field.fieldTypeBlobContent !== 'text') {
                //             context.fieldsContainBlobOrImage = true;
                //         }
                //     }
                //
                //     if (field.fieldValidate) {
                //         context.validation = true;
                //     }
                // });
                context.hasUserField = context.saveUserSnapshot = false;
                // Load in-memory data for relationships
                context.relationships.forEach((relationship) => {
                    if (_.isUndefined(relationship.relationshipNameCapitalized)) {
                        relationship.relationshipNameCapitalized = _.upperFirst(relationship.relationshipName);
                    }

                    if (_.isUndefined(relationship.relationshipNameCapitalizedPlural)) {
                        if (relationship.relationshipName.length > 1) {
                            relationship.relationshipNameCapitalizedPlural = pluralize(_.upperFirst(relationship.relationshipName));
                        } else {
                            relationship.relationshipNameCapitalizedPlural = _.upperFirst(pluralize(relationship.relationshipName));
                        }
                    }

                    if (_.isUndefined(relationship.relationshipNameHumanized)) {
                        relationship.relationshipNameHumanized = _.startCase(relationship.relationshipName);
                    }

                    if (_.isUndefined(relationship.relationshipNamePlural)) {
                        relationship.relationshipNamePlural = pluralize(relationship.relationshipName);
                    }

                    if (_.isUndefined(relationship.relationshipFieldName)) {
                        relationship.relationshipFieldName = _.lowerFirst(relationship.relationshipName);
                    }

                    if (_.isUndefined(relationship.relationshipFieldNamePlural)) {
                        relationship.relationshipFieldNamePlural = pluralize(_.lowerFirst(relationship.relationshipName));
                    }

                    if (
                        _.isUndefined(relationship.otherEntityRelationshipNamePlural) &&
                        (relationship.relationshipType === 'one-to-many' ||
                            (relationship.relationshipType === 'many-to-many' && relationship.ownerSide === false) ||
                            (relationship.relationshipType === 'one-to-one' && relationship.otherEntityName.toLowerCase() !== 'user'))
                    ) {
                        relationship.otherEntityRelationshipNamePlural = pluralize(relationship.otherEntityRelationshipName);
                    }

                    if (_.isUndefined(relationship.otherEntityRelationshipNameCapitalized)) {
                        relationship.otherEntityRelationshipNameCapitalized = _.upperFirst(relationship.otherEntityRelationshipName);
                    }

                    if (_.isUndefined(relationship.otherEntityRelationshipNameCapitalizedPlural)) {
                        relationship.otherEntityRelationshipNameCapitalizedPlural = pluralize(_.upperFirst(relationship.otherEntityRelationshipName));
                    }

                    const otherEntityName = relationship.otherEntityName;
                    const otherEntityData = this.getEntityJson(otherEntityName);
                    if (otherEntityData && otherEntityData.microserviceName && !otherEntityData.clientRootFolder) {
                        otherEntityData.clientRootFolder = otherEntityData.microserviceName;
                    }
                    const jhiTablePrefix = context.jhiTablePrefix;

                    if (context.dto && context.dto === 'mapstruct') {
                        if (
                            otherEntityData &&
                            (!otherEntityData.dto || otherEntityData.dto !== 'mapstruct') &&
                            otherEntityName !== 'user'
                        ) {
                            this.warning(chalk.red(`This entity has the DTO option, and it has a relationship with entity "${otherEntityName}" that doesn't have the DTO option. This will result in an error.`));
                        }
                    }

                    if (otherEntityName === 'user') {
                        relationship.otherEntityTableName = `${jhiTablePrefix}_user`;
                        context.hasUserField = true;
                    } else {
                        relationship.otherEntityTableName = otherEntityData ? otherEntityData.entityTableName : null;
                        if (!relationship.otherEntityTableName) {
                            relationship.otherEntityTableName = this.getTableName(otherEntityName);
                        }
                        if (jhiCore.isReservedTableName(relationship.otherEntityTableName, context.prodDatabaseType)) {
                            const otherEntityTableName = relationship.otherEntityTableName;
                            relationship.otherEntityTableName = `${jhiTablePrefix}_${otherEntityTableName}`;
                        }
                    }
                    context.saveUserSnapshot =
                        context.applicationType === 'microservice' && context.authenticationType === 'oauth2' && context.hasUserField;

                    if (_.isUndefined(relationship.otherEntityNamePlural)) {
                        relationship.otherEntityNamePlural = pluralize(relationship.otherEntityName);
                    }

                    if (_.isUndefined(relationship.otherEntityNameCapitalized)) {
                        relationship.otherEntityNameCapitalized = _.upperFirst(relationship.otherEntityName);
                    }

                    if (_.isUndefined(relationship.otherEntityRelationshipNamePlural)) {
                        if (relationship.relationshipType === 'many-to-one') {
                            if (otherEntityData && otherEntityData.relationships) {
                                otherEntityData.relationships.forEach((otherRelationship) => {
                                    if (
                                        _.upperFirst(otherRelationship.otherEntityName) === entityName &&
                                        otherRelationship.otherEntityRelationshipName === relationship.relationshipName &&
                                        otherRelationship.relationshipType === 'one-to-many'
                                    ) {
                                        relationship.otherEntityRelationshipName = otherRelationship.relationshipName;
                                        relationship.otherEntityRelationshipNamePlural = pluralize(otherRelationship.relationshipName);
                                    }
                                });
                            }
                        }
                    }

                    if (_.isUndefined(relationship.otherEntityAngularName)) {
                        if (relationship.otherEntityNameCapitalized !== 'User') {
                            const otherEntityAngularSuffix = otherEntityData ? otherEntityData.angularJSSuffix || '' : '';
                            relationship.otherEntityAngularName =
                                _.upperFirst(relationship.otherEntityName) + this.upperFirstCamelCase(otherEntityAngularSuffix);
                        } else {
                            relationship.otherEntityAngularName = 'User';
                        }
                    }

                    if (_.isUndefined(relationship.otherEntityNameCapitalizedPlural)) {
                        relationship.otherEntityNameCapitalizedPlural = pluralize(_.upperFirst(relationship.otherEntityName));
                    }

                    if (_.isUndefined(relationship.otherEntityFieldCapitalized)) {
                        relationship.otherEntityFieldCapitalized = _.upperFirst(relationship.otherEntityField);
                    }

                    if (_.isUndefined(relationship.otherEntityStateName)) {
                        relationship.otherEntityStateName = _.kebabCase(relationship.otherEntityAngularName);
                    }
                    if (_.isUndefined(relationship.otherEntityModuleName)) {
                        if (relationship.otherEntityNameCapitalized !== 'User') {
                            relationship.otherEntityModuleName = `${context.angularXAppName +
                            relationship.otherEntityNameCapitalized}Module`;
                            relationship.otherEntityFileName = _.kebabCase(relationship.otherEntityAngularName);
                            if (context.skipUiGrouping || otherEntityData === undefined || otherEntityData.clientRootFolder === undefined) {
                                relationship.otherEntityClientRootFolder = '';
                            } else {
                                relationship.otherEntityClientRootFolder = `${otherEntityData.clientRootFolder}/`;
                            }
                            if (otherEntityData !== undefined && otherEntityData.clientRootFolder) {
                                if (context.clientRootFolder === otherEntityData.clientRootFolder) {
                                    relationship.otherEntityModulePath = relationship.otherEntityFileName;
                                } else {
                                    relationship.otherEntityModulePath = `${context.entityParentPathAddition ? `${context.entityParentPathAddition}/` : ''}${otherEntityData.clientRootFolder}/${relationship.otherEntityFileName}`;
                                }
                                relationship.otherEntityModelName = `${otherEntityData.clientRootFolder}/${relationship.otherEntityFileName}`;
                                relationship.otherEntityPath = `${otherEntityData.clientRootFolder}/${relationship.otherEntityFileName}`;
                            } else {
                                relationship.otherEntityModulePath = `${context.entityParentPathAddition ? `${context.entityParentPathAddition}/` : ''}${relationship.otherEntityFileName}`;
                                relationship.otherEntityModelName = relationship.otherEntityFileName;
                                relationship.otherEntityPath = relationship.otherEntityFileName;
                            }
                        } else {
                            relationship.otherEntityModuleName = `${context.angularXAppName}SharedModule`;
                            relationship.otherEntityModulePath = 'app/core';
                        }
                    }
                    // Load in-memory data for root
                    if (relationship.relationshipType === 'many-to-many' && relationship.ownerSide) {
                        context.fieldsContainOwnerManyToMany = true;
                    } else if (relationship.relationshipType === 'one-to-one' && !relationship.ownerSide) {
                        context.fieldsContainNoOwnerOneToOne = true;
                    } else if (relationship.relationshipType === 'one-to-one' && relationship.ownerSide) {
                        context.fieldsContainOwnerOneToOne = true;
                    } else if (relationship.relationshipType === 'one-to-many') {
                        context.fieldsContainOneToMany = true;
                    } else if (relationship.relationshipType === 'many-to-one') {
                        context.fieldsContainManyToOne = true;
                    }

                    if (relationship.relationshipValidateRules && relationship.relationshipValidateRules.includes('required')) {
                        if (entityName.toLowerCase() === relationship.otherEntityName.toLowerCase()) {
                            this.warning(chalk.red('Required relationships to the same entity are not supported.'));
                        } else {
                            relationship.relationshipValidate = relationship.relationshipRequired = context.validation = true;
                        }
                    }

                    const entityType = relationship.otherEntityNameCapitalized;
                    if (!context.differentTypes.includes(entityType)) {
                        context.differentTypes.push(entityType);
                    }
                    if (!context.differentRelationships[entityType]) {
                        context.differentRelationships[entityType] = [];
                    }
                    context.differentRelationships[entityType].push(relationship);
                });

                context.pkType = this.getPkType(context.databaseType);
            },

            insight() {
                // track insights
                const context = this.context;

                statistics.sendEntityStats(
                    // context.fields.length,
                    context.relationships.length,
                    context.pagination,
                    context.dto,
                    context.service,
                    context.fluentMethods
                );
            }
        };
    }

    get writing() {
        return {
            updateFiles() {
                // read config from .yo-rc.json
                this.baseName = this.jhipsterAppConfig.baseName;
                this.packageName = this.jhipsterAppConfig.packageName;
                this.packageFolder = this.jhipsterAppConfig.packageFolder;
                this.clientFramework = this.jhipsterAppConfig.clientFramework;
                this.clientPackageManager = this.jhipsterAppConfig.clientPackageManager;
                this.buildTool = this.jhipsterAppConfig.buildTool;

                // use function in generator-base.js from generator-jhipster
                this.angularAppName = this.getAngularAppName();

                // const entityName = this.entityConfig.entityClass;
                // show all variables
                // do your stuff here
            },

            writeFiles() {
                // function to use directly template
                this.template = function (source, destination) {
                    this.fs.copyTpl(
                        this.templatePath(source),
                        this.destinationPath(destination),
                        this
                    );
                };
                this.template('doc/post.all.md.ejs', `doc/${this.context.name}/post.all.md`, this, this.context);
                this.template('doc/get.md.ejs', `doc/${this.context.name}/get.md`, this, this.context);
                this.template('doc/getone.md.ejs', `doc/${this.context.name}/getone.md`, this, this.context);
                this.template('doc/post.md.ejs', `doc/${this.context.name}/post.md`, this, this.context);
                this.template('doc/put.md.ejs', `doc/${this.context.name}/put.md`, this, this.context);
                this.template('doc/delete.md.ejs', `doc/${this.context.name}/delete.md`, this, this.context);
                this.template('src/main/java/package/service/extra/EntityExtraService.java.ejs', `src/main/java/${this.packageFolder}/service/extra/${this.context.name}ExtraService.java`, this, this.context);
                this.template('src/main/java/package/service/impl/extra/EntityExtraServiceImpl.java.ejs', `src/main/java/${this.packageFolder}/service/impl/extra/${this.context.name}ExtraServiceImpl.java`, this, this.context);
                this.template('src/main/java/package/web/rest/extra/EntityExtraResource.java.ejs', `src/main/java/${this.packageFolder}/web/rest/extra/${this.context.name}ExtraResource.java`, this, this.context);

            },

            // updateConfig() {
            //     this.updateEntityConfig(this.entityConfig.filename, 'yourOptionKey', this.yourOptionKey);
            // }
        };
    }

    end() {
        if (this.yourOptionKey) {
            this.log(`\n${chalk.bold.green('rain enabled')}`);
        }
    }
};
